param (
    [string]$MountPoint = $env:SystemDrive,
    [string]$RegistryPath = 'HKLM:\SOFTWARE\Policies\Microsoft'
)

#####################################

function Check-Prerequisites {
    Write-Verbose "Checking prerequisites..."
    
    # Check Windows Version for Windows 10 or 11
    $global:WindowsVer = Get-WmiObject -Query 'select * from Win32_OperatingSystem where (Version like "10.%" or Version like "11.%") and ProductType = "1"' -ErrorAction Stop
    if (-not $global:WindowsVer) {
        Write-Host "Unsupported Windows version. Exiting."
        exit 1
    }

    # Check TPM Status
    $global:TPMStatus = Get-WmiObject win32_tpm -Namespace root\cimv2\security\microsofttpm -ErrorAction Stop
    if ($global:TPMStatus -and $global:TPMStatus.SpecVersion -eq $null) {
        Write-Host "TPM is not enabled or available. Exiting."
        exit 1
    }

    # Additional checks can be added here like Secure Boot, Disk Partition type, etc.

    # If we've made it here, all checks have passed.
    Write-Verbose "All prerequisites met."
}

#####################################

function Initialize-TPM {
    Write-Verbose "Initializing TPM..."
    
    if (-not $global:WindowsVer) {
        Write-Host "Unsupported Windows version. Exiting."
        exit 1
    }

    if ($global:TPMStatus -and $global:TPMStatus.SpecVersion -eq $null) {
        Write-Host "TPM is not enabled or available. Exiting."
        exit 1
    }

    if ($global:TPMStatus.IsEnabled_InitialValue -eq $false) {
        try {
            Initialize-Tpm -AllowClear -AllowPhysicalPresence -ErrorAction Stop
            Write-Verbose "TPM initialized successfully."
        }
        catch {
            Write-Host "Failed to initialize TPM. Exiting."
            exit 1
        }
    }
    else {
        Write-Verbose "TPM is already initialized."
    }
}

#####################################
# The Configure-Registry function sets up various registry settings to configure BitLocker according to organizational standards. 
# 1. It enables the backup of BitLocker recovery information to Active Directory Domain Services (AD DS), a common practice to ensure 
#    recovery information is stored securely. 
# 2. The encryption methods specified are standard for BitLocker, with values '00000003' and '00000006' corresponding to AES 128-bit 
#    with Diffuser and AES 256-bit with Diffuser, respectively, which are strong encryption methods.
# 3. Recovery options are configured for both the operating system drive and fixed data drives to ensure data can be recovered in case 
#    of issues like forgotten passwords or PINs.
# 4. The function allows secure boot for integrity checking to ensure the integrity of the system during boot.
# 5. Both a recovery password and a recovery key are enabled, providing two methods for recovering data, thus enhancing the flexibility 
#    and robustness of the recovery process.
# 6. The recovery options page is hidden, possibly based on a security requirement to prevent users from accidentally or intentionally 
#    disabling protection or changing settings. This setup seems fairly standard for a controlled and secure environment, particularly 
#    within organizations with specific security policies and practices around data protection and recovery.

function Configure-Registry {
    Write-Verbose "Configuring registry..."
    $BitLockerRegLoc = "$global:RegistryPath\FVE"
    if (-not (Test-Path $BitLockerRegLoc)) {
        New-Item -Path "$global:RegistryPath" -Name 'FVE'
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'ActiveDirectoryBackup' -Value '00000001' -PropertyType DWORD # Enables backup of BitLocker recovery information to AD DS
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'RequireActiveDirectoryBackup' -Value '00000000' -PropertyType DWORD # Does not require backup to AD DS
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'ActiveDirectoryInfoToStore' -Value '00000001' -PropertyType DWORD # Specifies the type of information to be backed up to AD DS
        # The following settings  configure the encryption method to be used by BitLocker.
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'EncryptionMethodNoDiffuser' -Value '00000003' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'EncryptionMethodWithXtsOs' -Value '00000006' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'EncryptionMethodWithXtsFdv' -Value '00000006' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'EncryptionMethodWithXtsRdv' -Value '00000003' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'EncryptionMethod' -Value '00000003' -PropertyType DWORD
        # The following settings  configure recovery options for the OS drive.
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSRecovery' -Value '00000001' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSManageDRA' -Value '00000000' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSRecoveryPassword' -Value '00000002' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSRecoveryKey' -Value '00000002' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSHideRecoveryPage' -Value '00000001' -PropertyType DWORD # Likely hides the recovery options page
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSActiveDirectoryBackup' -Value '00000001' -PropertyType DWORD #  enables backup of recovery information to AD DS for the OS drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSActiveDirectoryInfoToStore' -Value '00000001' -PropertyType DWORD #  specifies the type of information to be backed up to AD DS for the OS drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSRequireActiveDirectoryBackup' -Value '00000000' -PropertyType DWORD #  does not require backup to AD DS for the OS drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSAllowSecureBootForIntegrity' -Value '00000001' -PropertyType DWORD #  allows secure boot for integrity checking
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'OSEncryptionType' -Value '00000001' -PropertyType DWORD
        # The following settings likely configure recovery options for the fixed data drive.
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVRecovery' -Value '00000001' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVManageDRA' -Value '00000000' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVRecoveryPassword' -Value '00000002' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVRecoveryKey' -Value '00000002' -PropertyType DWORD
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVHideRecoveryPage' -Value '00000001' -PropertyType DWORD #  hides the recovery options page for the fixed data drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVActiveDirectoryBackup' -Value '00000001' -PropertyType DWORD #  enables backup of recovery information to AD DS for the fixed data drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVActiveDirectoryInfoToStore' -Value '00000001' -PropertyType DWORD #  specifies the type of information to be backed up to AD DS for the fixed data drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVRequireActiveDirectoryBackup' -Value '00000000' -PropertyType DWORD #  does not require backup to AD DS for the fixed data drive
        New-ItemProperty -Path "$BitLockerRegLoc" -Name 'FDVEncryptionType' -Value '00000001' -PropertyType DWORD
    }
}

#####################################

function Enable-BitLocker {
    Write-Verbose "Checking if BitLocker can be enabled..."

    if (-not $global:WindowsVer) {
        Write-Host "Unsupported Windows version. Exiting."
        exit 1
    }

    if (-not $global:TPMStatus.IsEnabled_InitialValue) {
        Write-Host "TPM is not enabled. Exiting."
        exit 1
    }

    $BitLockerReadyDrive = Get-BitLockerVolume -MountPoint $global:MountPoint -ErrorAction SilentlyContinue
    if (-not $BitLockerReadyDrive) {
        Write-Host "Specified mount point is not ready for BitLocker. Exiting."
        exit 1
    }

    $BitLockerDecrypted = $BitLockerReadyDrive.VolumeStatus -eq "FullyDecrypted"
    if (-not $BitLockerDecrypted) {
        Write-Host "Drive is not fully decrypted. Exiting."
        exit 1
    }

    Write-Verbose "Enabling BitLocker..."
    try {
        Add-BitLockerKeyProtector -MountPoint $global:MountPoint -TpmProtector -ErrorAction Stop
        Enable-BitLocker -MountPoint $global:MountPoint -RecoveryPasswordProtector -ErrorAction Stop
        Write-Verbose "BitLocker enabled successfully."
    }
    catch {
        Write-Host "Failed to enable BitLocker. Exiting."
        exit 1
    }
}


#####################################

function Backup-RecoveryKeys {
    Write-Verbose "Starting to backup recovery keys..."
    $BLVS = Get-BitLockerVolume | Where-Object {$_.KeyProtector | Where-Object {$_.KeyProtectorType -eq 'RecoveryPassword'}} -ErrorAction SilentlyContinue
    
    if (-not $BLVS) {
        Write-Host "No BitLocker Volumes found with RecoveryPassword. Exiting."
        exit 1
    }
    
    # Check for Active Directory
    $isDomainJoined = $false
    try {
        $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
        if ($domain) {
            $isDomainJoined = $true
        }
    } catch {
        $isDomainJoined = $false
    }

    if (-not $isDomainJoined) {
        Write-Host "Machine is not bound to Active Directory. Backup not required."
        return
    }
    
    ForEach ($BLV in $BLVS) {
        $Keys = $BLV | Select-Object -ExpandProperty KeyProtector | Where-Object {$_.KeyProtectorType -eq 'RecoveryPassword'}
        
        if ($Keys) {
            ForEach ($Key in $Keys) {
                try {
                    Backup-BitLockerKeyProtector -MountPoint $BLV.MountPoint -KeyProtectorID $Key.KeyProtectorId -ErrorAction Stop
                    Write-Verbose "Successfully backed up key for $($BLV.MountPoint)"
                }
                catch {
                    Write-Host "Failed to backup key for $($BLV.MountPoint). Exiting."
                    exit 1
                }
            }
        } else {
            Write-Host "No Recovery Keys found for $($BLV.MountPoint). Exiting."
            exit 1
        }
    }
}


#####################################

function Invoke-FileWaveVerify {
    Write-Verbose "Executing FileWave Client Verify..."
    
    $programFilesPath = if ([IntPtr]::Size -eq 8) {
        [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFilesX86)
    } else {
        [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)
    }

    $FileWavePath = Join-Path -Path $programFilesPath -ChildPath 'FileWave\fwGUI.exe'
    
    if (Test-Path -Path $FileWavePath) {
        Write-Output "Executing FileWave Client Verify using path: $FileWavePath"
        Start-Process -FilePath $FileWavePath -ArgumentList '--verify', '--silent' -NoNewWindow -Wait
        Write-Output "FileWave Client Verify executed. Waiting for 120 seconds..."
        Start-Sleep -Seconds 120
        Write-Output "120-second wait completed."
    }
    else {
        Write-Warning "FileWave Client not found at $FileWavePath"
    }
}



#####################################

# Call the functions in sequence
Check-Prerequisites
Initialize-TPM
Configure-Registry
Enable-BitLocker
Backup-RecoveryKeys
Invoke-FileWaveVerify

exit 0